package com.rem.redis.service;

import cn.hutool.core.util.IdUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author Rem
 * @date 2022-05-23
 */

@Service
public class LockTestService {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    RedissonClient redissonClient;

    // 锁的键值
    private static String lockKey = "orderLock";


    /**
     * redis setIfAbsent lock
     * 存在问题：
     * *    1.redis集群/哨兵模式下
     * *    * - 客户端 A 在master 节点获取锁成功。
     * *    * - 还没有把获取锁的信息同步到 salve 的时候，master 宕机
     * *    * - salve 被选举为 新的 master ，这个时候客户端 A 的获取锁的数据是不存在 salve 中的。
     * *    * - 其他的客户端就可以获取 客户端 A 持有的 锁。
     * *    2.高并发下
     * *    * -  if (clientId.equals(redisTemplate.opsForValue().get(lockKey)))执行完成后锁过期了
     * *    * - 下面执行删除锁，就会删除其他线程的锁
     *
     * @return
     */
    public String setIfAbsentTransaction() {
        // 设置客户端id
        String clientId = IdUtil.fastSimpleUUID();
        try {
            // 设置锁    如果redis中已经存在 lockKey 则会添加失败
            Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS);
            if (result == null || !result) {
                return "error";
            }

            //具体业务
            Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
            if (stock == null) {
                return ">>>>>>>> 数据异常请稍后重试";
            }
            if (stock > 0) {
                Long lastNumber = redisTemplate.opsForValue().decrement("stock");
                System.out.println(">>>>>>>>>>> 扣除库存成功，剩余：" + lastNumber);
            } else {
                System.out.println(">>>>>>>>>>> 扣除库存失败，库存不足");
            }
        } finally {
            // 如果客户端id相等
            if (clientId.equals(redisTemplate.opsForValue().get(lockKey))) {
                redisTemplate.delete(lockKey);
            }
        }
        return "success";
    }


    /**
     * redisson lock
     * 支持集群模式，支持锁根据业务时长自动延迟释放
     * ]
     *
     * @return
     */
    public String redissonLock() {
        RLock lock = redissonClient.getLock(lockKey);
        boolean locked = lock.tryLock();
        try {
            if (locked) {
                //具体业务
                Integer stock = (Integer) redisTemplate.opsForValue().get("stock");
                if (stock == null) {
                    return ">>>>>>>> 数据异常请稍后重试";
                }
                if (stock > 0) {
                    Long lastNumber = redisTemplate.opsForValue().decrement("stock");
                    System.out.println(">>>>>>>>>>> 扣除库存成功，剩余：" + lastNumber);
                } else {
                    System.out.println(">>>>>>>>>>> 扣除库存失败，库存不足");
                }
            }
        } catch (Throwable throwable) {
            return "error";
        } finally {
            if (lock.isLocked()) {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }

        return "success";
    }
}
